﻿// assets/Scripts/manager/WindowManager.ts

import { Singleton } from "../utils/Singleton";
import Core from "../Core";
import { GameConst } from "../constant/GameConst";
import MyComponent from "../common/MyComponent";

const { ccclass } = cc._decorator;

// ==================== 动画配置接口 ====================
type OpenAnim =
  | "fade"
  | "scale"
  | "slideLeft"
  | "slideRight"
  | "slideUp"
  | "slideDown"
  | "none";
type CloseAnim =
  | "fade"
  | "scale"
  | "slideLeft"
  | "slideRight"
  | "slideUp"
  | "slideDown"
  | "none";

interface WindowAnimation {
  open?: OpenAnim;
  close?: CloseAnim;
  duration?: number;
}

// open 时传入的窗口信息（增强版）
export interface WindowInfo extends WindowAnimation {
  name: string;
  path: string;
  openSound?: boolean;
  closeSound?: boolean;
}

@ccclass
export default class WindowManager extends Singleton<WindowManager> {
  private _windowContainer: cc.Node | null = null;
  private _windows: cc.Node[] = [];
  private _showlist: { [name: string]: boolean } = {};
  // ❌ 可以删除或保留（用于兼容旧逻辑）
  // private _windowPool: Map<string, cc.Node[]> = new Map();
  private _prefabCache: Map<string, cc.Prefab> = new Map(); // ✅ 缓存 prefab

  // 默认动画配置
  private readonly DEFAULT_ANIM: WindowAnimation = {
    open: "scale",
    close: "fade",
    duration: 0.2,
  };

  // 在 WindowManager 内部定义
  private readonly WINDOW_TYPE_ANIM: { [type: number]: WindowAnimation } = {
    [GameConst.WIN_TYPE.FULL]: {
      open: "fade", // 全屏：淡入
      close: "fade", // 全屏：淡出
      duration: 0.3,
    },
    [GameConst.WIN_TYPE.POPUP]: {
      open: "scale", // 弹窗：缩放弹出
      close: "scale", // 弹窗：缩小关闭
      duration: 0.2,
    },
    [GameConst.WIN_TYPE.TOAST]: {
      open: "slideUp",
      close: "fade",
      duration: 0.15,
    },
    // 其他类型可扩展
  };

  public register(container: cc.Node): void {
    if (!container) {
      cc.error("WindowManager.register: container is null or undefined");
      return;
    }
    this._windowContainer = container;
  }

  public get container(): cc.Node | null {
    return this._windowContainer;
  }

  // ==================== 打开窗口（支持动画 + Promise）====================
  public async open(
    info: WindowInfo,
    showData: any = null,
    closeOther: boolean = false,
    closeCallback?: () => void
  ): Promise<MyComponent | null> {
    const { name, path, openSound = true, closeSound = true } = info;

    // 1. 去重
    if (this._showlist[name]) {
      cc.warn(`WindowManager.open: window "${name}" is already open.`);
      return null;
    }
    this._showlist[name] = true;

    // 2. 获取或创建窗口节点
    // ✅ 使用 ObjectPoolManager 获取节点
    let win: cc.Node | null = null;
    // 先尝试从 prefab 缓存中获取预制件
    let prefab = this._prefabCache.get(info.name);
    if (!prefab) {
      //无缓存，则从资源加载
      prefab = await Core.resHelper.load(info.path, cc.Prefab, "prefabs");

      if (!prefab) {
        cc.error(`WindowManager.open: Failed to load prefab at path: ${path}`);
        delete this._showlist[name];
        return null;
      }
      this._prefabCache.set(info.name, prefab); // ✅ 缓存
    }
    //从对象池获取节点
    win = Core.poolManager.getNode(prefab);
    if (!win) {
      cc.error(`Failed to get node from pool for: ${name}`);
      delete this._showlist[name];
      return null;
    }

    if (!this._windowContainer) {
      cc.error("WindowManager.open: window container not registered!");
      delete this._showlist[name];
      return null;
    }

    win.parent = this._windowContainer;
    console.log(win.parent.name);
    win.name = name;
    win.active = true;
    win.setPosition(0, 0);
    win.setScale(1);

    // 3. 添加到管理器
    this.addWin(win);
    this.sortWins(); // 设置 zIndex 和 active

    // 4. 获取组件
    const basePanel = win.getComponent(name) as MyComponent;
    if (!basePanel) {
      cc.error(`WindowManager.open: Component "${name}" not found on window.`);
      this.removeWin(name);
      return null;
    }
    // ===== 核心：确定最终动画配置 =====
    const winType = basePanel.winType;
    const typeAnim = this.WINDOW_TYPE_ANIM[winType] || this.DEFAULT_ANIM;
    const finalAnim: WindowInfo = {
      ...this.DEFAULT_ANIM,
      ...typeAnim,
      ...info, // 用户传的 info 优先级最高
    };
    // 保存到节点，供 close 时使用
    (win as any).__windowInfo = finalAnim;

    // 5. 设置数据和回调
    basePanel.data = showData;
    basePanel.closeCallback = closeCallback;

    // 6. 播放打开动画
    await this.playOpenAnimation(win, finalAnim);

    // 7. 显示数据
    basePanel.show(showData);

    // 8. 播放音效
    if (openSound) {
      Core.audioManager.playSFX("sound_openui");
    }

    // 10. 关闭其他窗口
    if (closeOther) {
      this.removeAll([name]);
    }

    return basePanel;
  }

  // ==================== 关闭窗口（支持动画 + Promise）====================
  public async removeWin(name: string): Promise<void> {
    if (!this._showlist[name]) return;
    delete this._showlist[name];

    const index = this._windows.findIndex((win) => win.name === name);
    if (index === -1) return;

    const win = this._windows[index];
    const winComp = win.getComponent(win.name) as MyComponent;

    if (winComp.winType === GameConst.WIN_TYPE.PERMANENT) {
      cc.warn(
        `WindowManager.removeWin: Cannot close permanent window "${name}".`
      );
      return;
    }

    // 1. 执行关闭回调
    winComp.closeCallback?.();
    winComp.closeCallback = null;

    // 2. 播放关闭动画
    const info = (win as any).__windowInfo || this.DEFAULT_ANIM;
    await this.playCloseAnimation(win, info);

    // 3. 音效
    Core.audioManager.playSFX("sound_closeui");

    // 4. 归还对象池
    win.active = false;
    win.parent = null;

    Core.poolManager.putNode(win);

    // 5. 从列表移除
    this._windows.splice(index, 1);

    // 6. 重新排序（激活新顶层窗口）
    this.sortWins();
  }

  // ==================== 动画播放 ====================
  private async playOpenAnimation(
    win: cc.Node,
    info: WindowInfo
  ): Promise<void> {
    const dur = info.duration ?? 0.2;
    return new Promise((resolve) => {
      const tween = cc.tween(win);
      // 初始状态
      switch (info.open) {
        case "fade":
          win.opacity = 0;
          tween.to(dur, { opacity: 255 }, { easing: "sineOut" });
          break;

        case "scale":
          win.setScale(0.8);
          win.opacity = 255;
          tween.to(dur, { scale: 1 }, { easing: "backOut" });
          break;

        case "slideLeft":
          win.x = -win.width;
          tween.to(dur, { x: 0 }, { easing: "sineOut" });
          break;

        case "slideRight":
          win.x = win.width;
          tween.to(dur, { x: 0 }, { easing: "sineOut" });
          break;

        case "slideUp":
          console.log("slideUp");
          win.y = -win.height;
          tween.to(dur, { y: 0 }, { easing: "sineOut" });
          break;

        case "slideDown":
          win.y = win.height;
          tween.to(dur, { y: 0 }, { easing: "sineOut" });
          break;

        default:
          win.active = true;
          resolve();
          return;
      }

      tween.call(() => resolve()).start();
    });
  }

  private async playCloseAnimation(
    win: cc.Node,
    info: WindowInfo
  ): Promise<void> {
    const anim = { ...this.DEFAULT_ANIM, ...info };
    const dur = anim.duration!;

    return new Promise((resolve) => {
      const tween = cc.tween(win);
      const winWidth = win.width;
      const winHeight = win.height;

      switch (anim.close) {
        case "fade":
          tween
            .to(dur, { opacity: 0 }, { easing: "sineIn" })
            .call(() => {
              win.active = false;
              win.setPosition(0, 0);
              resolve();
            })
            .start();
          break;

        case "scale":
          tween
            .to(dur, { scale: 0.8 }, { easing: "sineIn" })
            .call(() => {
              win.active = false;
              win.setPosition(0, 0);
              resolve();
            })
            .start();
          break;

        case "slideLeft":
          tween
            .to(dur, { x: -winWidth }, { easing: "sineIn" })
            .call(() => {
              win.active = false;
              win.setPosition(0, 0);
              resolve();
            })
            .start();
          break;

        case "slideRight":
          tween
            .to(dur, { x: winWidth }, { easing: "sineIn" })
            .call(() => {
              win.active = false;
              win.setPosition(0, 0);
              resolve();
            })
            .start();
          break;

        case "slideUp":
          tween
            .to(dur, { y: winHeight }, { easing: "sineIn" })
            .call(() => {
              win.active = false;
              win.setPosition(0, 0);
              resolve();
            })
            .start();
          break;

        case "slideDown":
          tween
            .to(dur, { y: -winHeight }, { easing: "sineIn" })
            .call(() => {
              win.active = false;
              win.setPosition(0, 0);
              resolve();
            })
            .start();
          break;

        default:
          // 立即关闭
          win.active = false;
          win.setPosition(0, 0);
          resolve();
          break;
      }
    });
  }

  // ==================== 窗口管理 ====================
  private addWin(win: cc.Node): void {
    const winComp = win.getComponent(win.name) as MyComponent;
    const layer = winComp.layer;

    let index = 0;
    for (let i = this._windows.length - 1; i >= 0; i--) {
      const tempWin = this._windows[i];
      const tempComp = tempWin.getComponent(tempWin.name) as MyComponent;
      if (tempComp.layer <= layer) {
        index = i + 1;
        break;
      }
    }
    this._windows.splice(index, 0, win);
  }

  private sortWins(): void {
    let hasFull = false;
    for (let i = this._windows.length - 1; i >= 0; i--) {
      const win = this._windows[i];
      const winComp = win.getComponent(win.name) as MyComponent;

      win.zIndex = ZIndexManager.getZIndex(winComp.layer);

      if (hasFull) {
        win.active = false;
      } else {
        win.active = true;
        hasFull = winComp.winType === GameConst.WIN_TYPE.FULL;
      }
    }
  }

  //==================== 预加载====================
  /**
   * 预加载多个窗口：包括 prefab 加载 + 节点预创建（放入对象池）
   * 调用后，这些窗口第一次打开时将完全无卡顿
   */
  public async preloadWindows(
    infos: Array<{ info: WindowInfo; count?: number }>
  ): Promise<void> {
    const promises = infos.map(async ({ info, count = 1 }) => {
      // 1. 检查 prefab 是否已缓存
      if (this._prefabCache.has(info.name)) {
        return; // 已加载，跳过
      }

      // 2. 加载 prefab（只加载一次）
      const prefab = (await Core.resHelper.load(
        info.path,
        cc.Prefab,
        "prefabs"
      )) as cc.Prefab;
      if (!prefab) {
        cc.error(
          `[WindowManager.preloadWindows] Failed to load prefab: ${info.path}`
        );
        return;
      }

      // 3. 缓存 prefab（open 时直接使用）
      this._prefabCache.set(info.name, prefab);

      // 4. 预创建 N 个实例并放入对象池（暖机步骤）
      for (let i = 0; i < count; i++) {
        const node = cc.instantiate(prefab);
        (node as any).__prefabRef = prefab; // ✅ 标记，putNode 才能识别
        node.name = info.name;
        node.active = false;
        if (node.parent) {
          node.removeFromParent();
        }

        // 放入对象池（自动管理 inactive、reset 状态）
        Core.poolManager.putNode(node);
      }

      cc.log(
        `[WindowManager] ✅ Preloaded: ${info.name} (prefab + ${count} instances)`
      );
    });

    // 并发加载所有 prefab
    await Promise.all(promises);

    cc.log(
      `[WindowManager] 🎉 preloadWindows completed: ${infos.length} windows`
    );
  }

  // 可扩展：从节点上获取动画配置（比如挂脚本）
  private getWindowInfo(win: cc.Node): WindowInfo {
    return (win as any).__windowInfo || this.DEFAULT_ANIM;
  }

  // ==================== 工具方法 ====================
  public isOpen(name: string): boolean {
    return this.getWin(name) != null;
  }

  public isShow(name: string): boolean {
    return this._showlist[name] === true;
  }

  public getWin(name: string): cc.Node | null {
    return this._windows.find((win) => win.name === name) || null;
  }

  public removeAll(exclude: string[] = []): void {
    for (let i = this._windows.length - 1; i >= 0; i--) {
      const win = this._windows[i];
      if (!exclude.includes(win.name)) {
        this.removeWin(win.name);
      }
    }
  }

  public clearAll(): void {
    this._windows = [];
    this._showlist = {};
  }
}

// ==================== ZIndexManager（保持不变）====================
export class ZIndexManager {
  private static baseZIndex: number = 100;
  private static layerOffset: number = 10;

  public static getZIndex(layer: number): number {
    return this.baseZIndex + layer * this.layerOffset;
  }

  public static setBaseZIndex(z: number): void {
    this.baseZIndex = z;
  }
}
